<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>CVS TEST</title>
</head>
<body>
	<div id="canvasHolder"  style="border:1px solid #cfcfcf;margin: 0,auto;">
		<canvas id="canvas" width="800" height="400" style="margin: 0,auto;">
		</canvas>
	</div>


	<script type="text/javascript">

		var canvas =  document.getElementById("canvas");
		var ctx = canvas.getContext("2d");

		//中 左 右
		function drawFlowNode(x,y , x1,y1, x2,y2,r1){
			ctx.save();

			var g1 = ctx.createRadialGradient(x, y, r1, x1, y1, 2);

			g1.addColorStop(0.1, 'rgba(216,221,49,1)');
			g1.addColorStop(0.5, 'rgba(216,221,49,0.8)');
			g1.addColorStop(0.7, 'rgba(216,221,49,0.4)');
			g1.addColorStop(1, 'rgba(216,221,49,0.2)');
			ctx.fillStyle = g1;

			ctx.beginPath();

			var xoffset = 0;
			var yoffset = r1;



			ctx.bezierCurveTo(x2,y2, x-xoffset, y-yoffset,x1,y1);
			ctx.bezierCurveTo(x1,y1, x-xoffset, y+yoffset,x2,y2);

			ctx.closePath();
			ctx.fill();
			ctx.restore();
		}


		function fillBackGround(){
			ctx.save();

			ctx.fillStyle = "rgba(0,0,0,1)";
			ctx.fillRect(0,0,canvas.width,canvas.height);

			ctx.beginPath();
			ctx.strokeStyle = "rgba(47,105,171,1)";
			ctx.moveTo(260,220);
			ctx.lineTo(540,100);
			ctx.closePath();
			ctx.stroke();

			ctx.restore();

		}

		var node1 = {x:260,y:220};
		var node2 = {x:540,y:100};
		function drawNode(){
			var nodeRadius = 5;
			ctx.save();


			var g1 = ctx.createRadialGradient(node1.x, node1.y, 0, node1.x, node1.y, nodeRadius);
			g1.addColorStop(0.1, 'rgba(91,230,114,1)');
			g1.addColorStop(1, 'rgba(91,230,114,0.2)');

			var g2 = ctx.createRadialGradient(node2.x, node2.y, 0, node2.x, node2.y, nodeRadius);
			g2.addColorStop(0.1, 'rgba(216,221,49,1)');
			g2.addColorStop(0.3, 'rgba(216,221,49,0.8)');
			g2.addColorStop(0.7, 'rgba(216,221,49,0.4)');
			g2.addColorStop(1, 'rgba(216,221,49,0.2)');

			ctx.beginPath();
			ctx.fillStyle = g1;
			ctx.arc(node1.x, node1.y, nodeRadius, 0, Math.PI * 2, true);
			ctx.closePath();
			ctx.fill();

			ctx.beginPath();
			ctx.fillStyle = g2;
			ctx.arc(node2.x, node2.y, nodeRadius, 0, Math.PI * 2, true);
			ctx.closePath();

			ctx.fill();

			ctx.restore();

			//二次贝塞尔曲线
			ctx.beginPath();
			ctx.strokeStyle = "rgba(202,58,215,1)";
			ctx.moveTo(node1.x, node1.y);
			ctx.quadraticCurveTo( (node1.x+node2.x)/2+40 , 
				(node1.y+node2.y)/2+40 ,node2.x, node2.y);
			ctx.stroke();

			//三次贝塞尔曲线
			ctx.beginPath();
			ctx.strokeStyle = "rgba(192,190,63,1)";
			ctx.moveTo(node1.x, node1.y);
			var cpx = (node1.x+node2.x)/2-10 ;
			var cpy = (node1.y+node2.y)/2-10;
			ctx.bezierCurveTo( cpx, cpy,cpx-100, cpy-100,node2.x, node2.y);
			ctx.stroke();

		}

		//水滴 
		function drawRaindrops(ctx,pointArray){
			var c = [91,230,114];
			var radius = (pointArray.length - 1);

			ctx.save();
			ctx.beginPath();
			for(var i=0; i<pointArray.length ;i++){
				var p = pointArray[i];
				if(p.x == -9999 || p.y == -9999){
					continue;
				}
				var tmpRadius =  radius*(i+1)/pointArray.length;
				var g1 = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y,tmpRadius);
				g1.addColorStop(0.1, 'rgba('+c[0]+','+c[1]+','+c[2]+',0.8)');
				g1.addColorStop(0.5, 'rgba('+c[0]+','+c[1]+','+c[2]+',0.5)');
				g1.addColorStop(1, 'rgba('+c[0]+','+c[1]+','+c[2]+',0.2)');
				ctx.fillStyle = g1;
				ctx.moveTo(p.x, p.y);
				ctx.arc(p.x, p.y, tmpRadius, 0, Math.PI * 2, true);
				ctx.fill();
			}
			ctx.restore();

		}


		var pa = [ ];

		var xValue = 100;
		for(var i=0;i<8;i++){

			pa.push({x:xValue,y:300});
			xValue = xValue + 4;
		};

		


		function Point2D(x, y) {
			this.x = x || 0.0;
			this.y = y || 0.0;
		}
		//根据四个点来生成一条bezier曲线计算公式
		function pointOnCubicBezier(cp,t){

			var pa = cp;
			var ax, bx, cx;
			var ay, by, cy;
			var tSquared, tCubed;
			var result = new Point2D;

			cx = 3.0 * (cp[1].x - cp[0].x);
			bx = 3.0 * (cp[2].x - cp[1].x) - cx;
			ax = cp[3].x - cp[0].x - cx - bx;

			cy = 3.0 * (cp[1].y - cp[0].y);
			by = 3.0 * (cp[2].y - cp[1].y) - cy;
			ay = cp[3].y - cp[0].y - cy - by;

			tSquared = t * t;
			tCubed = tSquared * t;

			result.x = (ax * tCubed) + (bx * tSquared) + (cx * t) + cp[0].x;
			result.y = (ay * tCubed) + (by * tSquared) + (cy * t) + cp[0].y;

			var tb = 1-t;
			// result.x = cp[0].x*tb*tb*tb + 3*cp[1].x*t*tb*tb + 3*cp[2].x*t*t*tb + t*t*t*cp[3].x;

			return result;
		}

		function ComputeBezier(cp) {
			var pointArray = [];
			var dt;
			var i;
			var numberOfPoints = 100;

			var al1 = (cp[0].x-cp[3].x);
			var al2 = (cp[0].y-cp[3].y);
			var al3 = Math.sqrt(al1*al1 + al2*al2);
			numberOfPoints = numberOfPoints*(al3/200);

			dt = 1.0 / (numberOfPoints - 1);

			for (i = 0; i < numberOfPoints; i++)
				pointArray[i] = pointOnCubicBezier(cp, i * dt);

			return pointArray;
		}

		var nodeTmpx = (node1.x+node2.x)/2-10 ;
		var nodeTmpy = (node1.y+node2.y)/2-10;
		var bezierArray = [
			{x:node1.x,y:node1.y},
			{x:nodeTmpx,y:nodeTmpy},
			{x:nodeTmpx - 100 ,y:nodeTmpy-100},
			{x:node2.x,y:node2.y}
		];



		//线性贝塞尔曲线上的点
		function computeLineBezier(points){
			var x1 = points[0].x;
			var y1 = points[0].y;
			var x2 = points[1].x;
			var y2 = points[1].y;

			var al1 = (x1 - x2);
			var al2 = (y1 - y2);
			var al3 = Math.sqrt(al1*al1 + al2*al2);

			var numberOfPoints = 100;
			numberOfPoints = numberOfPoints*(al3/200);

			var pointArray = [];
			var dt = 1.0 / (numberOfPoints - 1);

			for (i = 0; i < numberOfPoints; i++){

				var t = i*dt;
				var tmpx = 0;
				var tmpy = 0;
				// 计算当前t 的 point
				tmpx = x1 + (x2-x1)*t;
				tmpy = y1 + (y2-y1)*t;


				pointArray[i] = {x:tmpx,y:tmpy}
			}

			return pointArray;
		}

		//二次贝塞尔曲线上点
		function computeQuadraticArrayBezier(points){
			var x1 = points[0].x;
			var y1 = points[0].y;
			var x2 = points[1].x;
			var y2 = points[1].y;			
			var x3 = points[2].x;
			var y3 = points[2].y;

			var al1 = (x1 - x3);
			var al2 = (y1 - y3);
			var al3 = Math.sqrt(al1*al1 + al2*al2);

			var numberOfPoints = 100;
			numberOfPoints = numberOfPoints*(al3/200);

			var pointArray = [];
			var dt = 1.0 / (numberOfPoints - 1);

			for(var i=0; i<numberOfPoints ;i++){
				var t = i*dt;
				var tmpx = 0;
				var tmpy = 0;

				tmpx = (1-t)*(1-t)*x1 + 2*t*(1-t)*x2 + t*t*x3;
				tmpy = (1-t)*(1-t)*y1 + 2*t*(1-t)*y2 + t*t*y3;

				
				pointArray[i] = {x:tmpx,y:tmpy};
			}


			return pointArray;
		}

		var posIndex = 0;
		var posIndex2 = 0;
		var posIndex3 = 0;

		var pathPointArray = ComputeBezier(bezierArray);
		// var pathPointArray = createAvgBezierArray(bezierArray);
		var lineBizerArray = computeLineBezier([node1,node2]);
		var quadraticBizerArray = 
			computeQuadraticArrayBezier([node1,{x:(nodeTmpx+50),y:(nodeTmpy+50)},node2]);
		function draw(){
			fillBackGround();
			drawFlowNode(300,100,10,100,400,100,80);
			drawNode();
			drawRaindrops(ctx,pa);

			
			ctx.beginPath();

			if(posIndex >= pathPointArray.length){
				posIndex = 0;
			}
			var p1 = pathPointArray[posIndex];
			ctx.moveTo(p1.x, p1.y);
			ctx.fillStyle = "rgba(255,0,0,0.5)";
			ctx.arc(p1.x, p1.y, 5, 0, Math.PI * 2, true);


			if(posIndex2 >= lineBizerArray.length){
				posIndex2 = 0;
			}
			var p2 = lineBizerArray[posIndex2];
			ctx.moveTo(p2.x, p2.y);
			ctx.arc(p2.x, p2.y, 5, 0, Math.PI * 2, true);

			if(posIndex3 >= quadraticBizerArray.length){
				posIndex3 = 0;
			}
			var p3 = quadraticBizerArray[posIndex3];
			ctx.moveTo(p3.x, p3.y);
			ctx.arc(p3.x, p3.y, 5, 0, Math.PI * 2, true);
			
			ctx.fill();

			drawRaindropsFlow();
		}

		var raindropsPointArray = [];
		var rdPosIndex = 0;
		var rdCircleCount = 8;
		function drawRaindropsFlow(){
			var points = [
				node1,{x:(nodeTmpx+200),y:(nodeTmpy+200)},node2,
			];
			if(raindropsPointArray.length == 0){
				raindropsPointArray = computeQuadraticArrayBezier(points);
			}
			ctx.strokeStyle = 'rgba(122,27,161,1);';
			ctx.beginPath();
			ctx.moveTo(points[0].x,points[0].y);
			ctx.quadraticCurveTo(points[1].x,points[1].y,points[2].x,points[2].y);
			ctx.stroke();

			ctx.beginPath();
			if(rdPosIndex-rdCircleCount >= raindropsPointArray.length){
				rdPosIndex = 0;
			}
			var ps = [];

			for(var a=0;a<rdCircleCount;a++ ){
				var tmpPoint;

				var index = rdPosIndex - 7 + a;
				if(index >= raindropsPointArray.length || index<0){
					ps[a] = {x:-9999, y:-9999};
				}else{
					var vx = raindropsPointArray[index].x;
					var vy = raindropsPointArray[index].y;
					ps[a] = {x: vx , y: vy};
				}
			}

			drawRaindrops(ctx,ps);
		}

		function repaint(){
			ctx.clearRect(0, 0, canvas.width, canvas.height);
			draw();
		}

		draw();

		window.setInterval(function(){
			posIndex++;
			posIndex2++;
			posIndex3++;
			rdPosIndex++;
			repaint();
		},1000/24);



		//...............物体沿着贝塞尔曲线匀速运行 算法  

		var flowSpeed = 4;
		var bezierLen = 0;
		var numOfPoint = 0;
		//长度方程,使用Simpson积分算法  
		function bezierLength( t ){
			//分割份数 
			var stepCounts = numOfPoint*t; 

			//偶数
			if(stepCounts & 1) stepCounts++;

			if(stepCounts==0) return 0.0;  

			var halfCounts = stepCounts/2;  
		
			var sum1=0.0, sum2=0.0;

			var dStep = t/stepCounts;

			for(var i=0; i<halfCounts; i++) {
				sum1 += bezeSpeed((2*i+1)*dStep);
			}
			for(var i=1; i<halfCounts; i++){  
				sum2 += bezeSpeed((2*i)*dStep);  
			}

			return (bezeSpeed(0.0)+bezeSpeed(1.0)+2*sum2+4*sum1)*dStep/3.0;

		}

		function bezeSpeedX(t){
			var it = 1-t;
			var P0 = bezierArray[0];
			var P1 = bezierArray[1];
			var P2 = bezierArray[2];
			var P3 = bezierArray[3];
			return -3*P0.x*it*it + 3*P1.x*it*it 
				- 6*P1.x*it*t + 6*P2.x*it*t 
				- 3*P2.x*t*t + 3*P3.x*t*t; 

		}

		function bezeSpeedY(t){
			var it = 1-t;
			var P0 = bezierArray[0];
			var P1 = bezierArray[1];
			var P2 = bezierArray[2];
			var P3 = bezierArray[3];
			return -3*P0.x*it*it + 3*P1.x*it*it 
				- 6*P1.x*it*t + 6*P2.x*it*t 
				- 3*P2.x*t*t + 3*P3.x*t*t;
		}

		function bezeSpeed(t){
			var sx = bezeSpeedX(t);  
			var sy = bezeSpeedY(t);  
			return Math.sqrt(sx*sx+sy*sy);
		}


		function bezierEven(t){

			var len = t*bezierLen;

			var t1=t, t2;   
			do  
			{  
				t2 = t1 - (bezierLength(t1)-len)/bezierLength(t1);  
				if(Math.abs(t1-t2)<0.0000001) break;  
				t1=t2;  
			}while(true); 

			return t2;
		}


		function createAvgBezierArray(points){

			var pointArray = [];

			var xs = points[0].x;
			var ys = points[0].y;
			var xe = points[points.length-1].x;
			var ye = points[points.length-1].y;

			var al1 = (xs - xe);
			var al2 = (ys - ye);
			var al3 = Math.sqrt(al1*al1 + al2*al2);

			var numberOfPoints = 100;
			numOfPoint = numberOfPoints*(al3/200);
			bezierLen = bezierLength(1);

			numOfPoint = bezierLen / flowSpeed;
			var dt = 1.0 / (numOfPoint - 1);

			var tOld = 0;
			for(var i=0; i<numOfPoint ;i++){
				var t = i*dt;
			}
			alert(bezierLen +  "|| numberOfPoints:" + numOfPoint);
			return pointArray;
		}

		createAvgBezierArray(bezierArray);

	</script>
</body>
</html>